package bluetooth;

import java.io.IOException;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;

/**
 * Klasa implementujaca klienta bluetooth.
 * @author      Przemyslaw Bierut
 * @version     1.0
 */
public class Client implements DiscoveryListener, BtListener {

    private ClientListener clientListener;
    private UUID uuid;
    private int inquiryMode;
    private int connectionOptions;
    private BtVector deviceList;
    private DiscoveryAgent agent;
    private BtConnection btConnection;

    /**
     * Metoda ustawiajaca pola klasy.
     * @param _clientListener obiekt ktory bedzie nasluchiwal zdarzen wynikajacych
     * z funkcjonowania klienta.
     */
    public void createClient(ClientListener _clientListener) {
        clientListener = _clientListener;
        agent = getAgent();
        uuid = new UUID(0x1101);
        inquiryMode = DiscoveryAgent.GIAC;
        connectionOptions = ServiceRecord.NOAUTHENTICATE_NOENCRYPT;
        deviceList = new BtVector();
        log("Client created");
    }

    /**
     * Metoda startujaca wyszukiwanie urzadzen bluetooth.
     */
    public void startDeviceInquiry() {
        try {
            log("Start inquiry method - this will take few seconds...");
            agent.startInquiry(inquiryMode, this);
        } catch (Exception e) {
            log(e);
        }
    }

    /**
     * Metoda informujaca o odnalezieniu nowgo urzadzenia.
     * @param btDevice znalezione urzadzenie
     * @param cod klasa urzadzenia
     */
    public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
        log("A device discovered (" + getDeviceInfo(btDevice) + ")");
        deviceList.add(btDevice);
    }

    /**
     * Metoda informujaca o zakonczeniu wyszukiwania urzadzen bluetooth.
     * @param discType status zakonczonego wyszukiwania
     */
    public void inquiryCompleted(int discType) {
        log("Inquiry compeleted. Please select device from combo box.");
        clientListener.deviceListShown(deviceList);
    }

    /**
     * Metoda startujaca wyszukiwanie uslug na danym urzadzniu.
     * @param device urzadzenie na ktorym beda wyszukiwane uslugi.
     */
    public void startServiceSearch(RemoteDevice device) {
        try {
            log("Start search service from " + getDeviceInfo(device));
            agent.searchServices(null, new UUID[]{uuid}, device, this);
        } catch (Exception e) {
            log(e);
        }
    }

    /**
     * Metoda informujaca o odnalezieniu uslugi na urzadzeniu.
     */
    public void servicesDiscovered(int transId, ServiceRecord[] records) {
        log("Service discovered.");
        btConnection = new BtConnection(records[0].getConnectionURL(connectionOptions, false), this);
    }

    /**
     * Metoda informujaca o zakonczeniu wyszukiwania uslug.
     */
    public void serviceSearchCompleted(int transID, int respCode) {
        log("Service search completed.");
        clientListener.connetionEstablished();
    }

    /**
     * Metoda umozliwiajaca wysylanie danych na serwer.
     * @param data dane do wyslania
     */
    public void sendData(String data) {
        btConnection.sendData(data);
    }

    /**
     * Metoda przerywajaca wyszukiwanie uslug.
     */
    public void stoptServiceSearch() {
        agent.cancelInquiry(this);
    }

    private void log(String msg) {
        clientListener.statusUpdate(msg + "\n");
    }

    private void log(Throwable e) {
        log(e.getMessage());
    }

    private DiscoveryAgent getAgent() {
        try {
            return LocalDevice.getLocalDevice().getDiscoveryAgent();
        } catch (BluetoothStateException e) {
            throw new Error(e.getMessage());
        }
    }

    private String getDeviceInfo(RemoteDevice btDevice) {
        String s = "<bez_nazwy>";
        try {
            s = btDevice.getFriendlyName(false) + " - " + btDevice.getBluetoothAddress();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return s;
    }

    /**
     * Metoda obslugujaca zerwanie polaczenie z serwerem.
     * @param deviceAdress
     */
    public void connectionLost(String deviceAdress) {
        btConnection = null;
        clientListener.connectionLost(deviceAdress);
    }

    /**
     * Metoda informujaca o otrzymaniu danych.
     * @param data otrzymane dane
     * @param btAdress adres urzadzenia na ktorej chcemy wyslac dane
     */
    public void dataReceived(String data, String btAdress) {
        clientListener.dataReceived(data, btAdress);
    }
}

